/*
 * Copyright (c) 2012 The Native Client Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef NATIVE_CLIENT_SRC_TRUSTED_SERVICE_RUNTIME_NACL_COPY_H_
#define NATIVE_CLIENT_SRC_TRUSTED_SERVICE_RUNTIME_NACL_COPY_H_

#include "native_client/src/include/build_config.h"
#include "native_client/src/include/nacl_base.h"
#include "native_client/src/include/nacl_compiler_annotations.h"
#include "native_client/src/include/portability.h"
#include "native_client/src/shared/platform/nacl_sync_checked.h"
#include "native_client/src/trusted/service_runtime/sel_ldr.h"

EXTERN_C_BEGIN

struct NaClApp;

/*
 * NaClCopyInFromUser copies a region of memory starting at
 * |src_usr_addr| of length |num_bytes| to a target buffer at
 * |dst_sys_ptr|, which is a pointer (in kernel/system address space).
 * The |src_usr_addr| is in user addresses and is checked to ensure
 * that it resides within the user's address space.  If it is not, 0
 * (false) is returned.  Otherwise, the address is properly translated
 * and the copy attempted.  Note that we do not (currently) check
 * whether the source memory region is actually mapped, so a trusted
 * code fault may occur.  If the function returns successfully, it
 * returns non-zero (true).  NB: the caller needs to translate failure
 * to -NACL_ABI_EFAULT or -NACL_ABI_EINVAL as appropriate.  (In Unix,
 * EFAULT is for when the syscall handler immediately uses the address
 * and is generated by triggering the kernel fault handler.)
 *
 * NB: |src_usr_addr| should be a 32-bit quantity and typically user
 * addresses are stored in 32-bit variables.  However, we accept an
 * uintptr_t since on 64-bit systems we may have relying code that
 * saved an user address in a 64-bit variable, and if such a value
 * were clobbered so that the low-order 32-bits would validate in
 * checks done here and elsewhere (due to the integral type narrowing)
 * but the high-order 32-bits are non-zero, and the relying code,
 * after having used the NaClCopy functions assumed that the value has
 * been checked and then uses it directly, then declaring the formal
 * parameter |src_usr_addr| as an uint32_t would not detect the error
 * but declaring it as uintptr_t would.
 */
int NaClCopyInFromUser(struct NaClApp *nap,
                       void           *dst_sys_ptr,
                       uintptr_t      src_usr_addr,
                       size_t         num_bytes) NACL_WUR;

int NaClCopyInFromUserAndDropLock(struct NaClApp *nap,
                                  void           *dst_sys_ptr,
                                  uintptr_t      src_usr_addr,
                                  size_t         num_bytes) NACL_WUR;

/*
 * Similarly to strncpy from untrusted address space to trusted
 * address space, except returns success/failure (bool as int) and the
 * destination string is always NUL terminated.  If the untrusted user
 * address is invalid or there isn't enough space, returns 0 with
 * either a zero-length string in the buffer or the partially copied
 * (but still NUL-terminated) string in the buffer respectively.
 * (Former case probably would result in EFAULT, whereas the latter
 * case would result in ENAMETOOLONG.)
 *
 * It is an error to invoke this with dst_buffer_bytes == 0.
 */
int NaClCopyInFromUserZStr(struct NaClApp *nap,
                           char           *dst_buffer,
                           size_t         dst_buffer_bytes,
                           uintptr_t      src_usr_addr) NACL_WUR;

/*
 * NaClCopyOutToUser perform the reverse analogous service as
 * NaClCopyInFromUser.
 */
int NaClCopyOutToUser(struct NaClApp  *nap,
                      uintptr_t       dst_usr_addr,
                      const void      *src_sys_ptr,
                      size_t          num_bytes) NACL_WUR;

/*
 * We use locking to prevent reads/writes of untrusted address space
 * while a hole is opened up in untrusted address space.
 *
 * Address space holes only occur on Windows, so we don't need this
 * locking in non-Windows builds, but we enable it in non-Windows
 * debug builds to help with earlier detection of deadlocks during
 * development.
 */
#if NACL_WINDOWS || defined(_DEBUG)

static INLINE void NaClCopyTakeLock(struct NaClApp *nap) {
  NaClXMutexLock(&nap->mu);
}

static INLINE void NaClCopyDropLock(struct NaClApp *nap) {
  NaClXMutexUnlock(&nap->mu);
}

#else

static INLINE void NaClCopyTakeLock(struct NaClApp *nap) {
  UNREFERENCED_PARAMETER(nap);
}

static INLINE void NaClCopyDropLock(struct NaClApp *nap) {
  UNREFERENCED_PARAMETER(nap);
}

#endif

EXTERN_C_END

#endif
